#include "logintask.h"
#include "libs/base/service/downloader.h"
#include "libs/base/utils/utils.h"
#include "libs/core/user/localuser.h"
#include "services/yingceservice.h"
#include "usersettings.h"
#include "yingce.h"

LoginTask::LoginTask(const yc::LoginUser& user, QObject* parent)
    : yc::Task { 0, true, 60000, parent }, user_(user), use_token_(true)
{
}

LoginTask::LoginTask(const QString& name, const QString& pwd, const QString& code, QObject* parent)
    : yc::Task { 0, true, 60000, parent },
      user_(name),
      password_(pwd),
      code_(code),
      use_token_(false)
{
}

void LoginTask::onRefresh(const yc::Request& request, const yc::Response& response)
{
    if (response.status() == yc::OK_CODE) {
        const QJsonObject jo = response.data().toObject();
        user_.updateData(jo);
        YingCeService::instance()->loginByToken(user_.name(), user_.accessToken(),
                                                yc::ResponseHandler(this, &LoginTask::onResponse));
    } else {
        YingCe::instance()->localUser()->changeLoginStatus(yc::LocalUser::LOGOUT_STATUS);
        Q_EMIT failed(yc::INTERNAL_SERVER_ERROR,
                      tr("Failed to login in, the error is \"%1\".").arg(response.msg()));
    }
}

void LoginTask::onResponse(const yc::Request& request, const yc::Response& response)
{
    Q_UNUSED(request)
    if (response.status() == yc::OK_CODE) {
        const QJsonObject jo = response.data().toObject();
        // user
        const QJsonObject jo_user = jo["user"].toObject();
        yc::LocalUser* user = YingCe::instance()->localUser();
        user->updateData(jo_user);
        // token
        const QJsonObject jo_token = jo["token"].toObject();
        UserSettings::instance().loginUser().updateData(jo_token);
        UserSettings::instance().loginUser().setDeviceId(user_.deviceId());
        UserSettings::instance().loginUser().setName(user_.name());
        YingCeService::instance()->setAuthentication(
                UserSettings::instance().loginUser().accessToken(),
                UserSettings::instance().loginUser().refreshToken());
        UserSettings::instance().save();
        const QDir red_dir = { UserSettings::instance().resDir() };
        if ((!user->avatar().isEmpty() && !red_dir.exists(user->avatar()))
            || (!user->cover().isEmpty() && !red_dir.exists(user->cover()))) {
            const QString root_url = UserSettings::instance().serviceUrls().cloud_download;
            yc::Downloader* t =
                    new yc::Downloader(YingCeService::instance(), root_url, red_dir, true, 0, this);
            connect(t, &yc::Downloader::finished, this, [this] {
                YingCe::instance()->localUser()->changeLoginStatus(yc::LocalUser::LOGGEDIN_STATUS);
                Q_EMIT finished();
            });
            connect(t, &yc::Downloader::failed, this, [this](int error, const QString& msg) {
                qDebug() << "Failed to download the avatar." << error << msg;
                YingCe::instance()->localUser()->changeLoginStatus(yc::LocalUser::LOGGEDIN_STATUS);
                Q_EMIT finished();
            });
            if (!user->cover().isEmpty() && !red_dir.exists(user->cover())) {
                t->addResource(yc::DownloadResource { user->cover(), user->cover(), 0 });
            }
            if (!user->avatar().isEmpty() && !red_dir.exists(user->avatar())) {
                t->addResource(yc::DownloadResource { user->avatar(), user->avatar(), 0 });
            }
            t->start();
        } else {
            // change status
            user->changeLoginStatus(yc::LocalUser::LOGGEDIN_STATUS);
            Q_EMIT finished();
        }
    } else {
        YingCe::instance()->localUser()->changeLoginStatus(yc::LocalUser::LOGOUT_STATUS);

        QJsonObject jo = response.data().toObject();
        code_ = jo["authCode"].toString();
        Q_EMIT failed(yc::INTERNAL_SERVER_ERROR,
                      tr("Failed to login in, the error is \"%1\".").arg(response.msg()));
    }
}

void LoginTask::startImpl()
{
    YingCe::instance()->localUser()->changeLoginStatus(yc::LocalUser::LOGINING_STATUS);

    if (use_token_) {
        QDateTime now = QDateTime::currentDateTime().addSecs(-5);
        if (user_.accessTokenExpire() >= now) {
            YingCeService::instance()->loginByToken(
                    user_.name(), user_.accessToken(),
                    yc::ResponseHandler(this, &LoginTask::onResponse));
        } else if (user_.refreshTokenExpire() >= now) {
            YingCeService::instance()->refreshToken(
                    user_.refreshToken(), yc::ResponseHandler(this, &LoginTask::onRefresh));
        } else {
            YingCe::instance()->localUser()->changeLoginStatus(yc::LocalUser::LOGOUT_STATUS);
            Q_EMIT failed(yc::TOKEN_EXPIRED, tr("Token is expired."));
        }
    } else {
        user_.setDeviceId(yc::baseutils::getDeviceId());
        YingCeService::instance()->loginByName(user_.name(), password_, code_, user_.deviceId(),
                                               yc::ResponseHandler(this, &LoginTask::onResponse));
    }
}
